package wci.frontend.pascal.parsers;

import static wci.frontend.pascal.PascalErrorCode.IDENTIFIER_UNDEFINED;
import static wci.frontend.pascal.PascalErrorCode.MISSING_RIGHT_PAREN;
import static wci.frontend.pascal.PascalErrorCode.UNEXPECTED_TOKEN;
import static wci.frontend.pascal.PascalTokenType.DIV;
import static wci.frontend.pascal.PascalTokenType.EQUALS;
import static wci.frontend.pascal.PascalTokenType.GREATER_EQUALS;
import static wci.frontend.pascal.PascalTokenType.GREATER_THAN;
import static wci.frontend.pascal.PascalTokenType.IDENTIFIER;
import static wci.frontend.pascal.PascalTokenType.INTEGER;
import static wci.frontend.pascal.PascalTokenType.LEFT_PAREN;
import static wci.frontend.pascal.PascalTokenType.LESS_EQUALS;
import static wci.frontend.pascal.PascalTokenType.LESS_THAN;
import static wci.frontend.pascal.PascalTokenType.MINUS;
import static wci.frontend.pascal.PascalTokenType.NOT_EQUALS;
import static wci.frontend.pascal.PascalTokenType.PLUS;
import static wci.frontend.pascal.PascalTokenType.REAL;
import static wci.frontend.pascal.PascalTokenType.RIGHT_PAREN;
import static wci.frontend.pascal.PascalTokenType.SLASH;
import static wci.frontend.pascal.PascalTokenType.STAR;
import static wci.frontend.pascal.PascalTokenType.STRING;
import static wci.intermediate.icodeimpl.ICodeKeyImpl.VALUE;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.ADD;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.EQ;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.FLOAT_DIVIDE;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.GE;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.GT;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.INTEGER_CONSTANT;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.INTEGER_DIVIDE;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.LE;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.LT;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.MULTIPLY;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.NE;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.NEGATE;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.REAL_CONSTANT;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.STRING_CONSTANT;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.SUBTRACT;

import java.util.EnumSet;
import java.util.HashMap;

import wci.frontend.Token;
import wci.frontend.TokenType;
import wci.frontend.pascal.PascalErrorCode;
import wci.frontend.pascal.PascalParserTD;
import wci.frontend.pascal.PascalTokenType;
import wci.intermediate.Definition;
import wci.intermediate.ICodeFactory;
import wci.intermediate.ICodeNode;
import wci.intermediate.ICodeNodeType;
import wci.intermediate.SymTabEntry;
import wci.intermediate.TypeFactory;
import wci.intermediate.TypeSpec;
import wci.intermediate.icodeimpl.ICodeNodeTypeImpl;
import wci.intermediate.symtabimpl.DefinitionImpl;
import wci.intermediate.symtabimpl.Predefined;
import wci.intermediate.symtabimpl.SymTabKeyImpl;
import wci.intermediate.typeimpl.TypeChecker;

/**
 * <h1>ExpressionParser</h1>
 *
 * <p>解析表达式，目前表达式较为简单</p>
 *
 * <p>Copyright (c) 2009 by Ronald Mak</p>
 * <p>For instructional purposes only.  No warranties.</p>
 */
public class ExpressionParser extends StatementParser
{
	static final EnumSet<PascalTokenType> EXPR_START_SET =
		        EnumSet.of(PLUS, MINUS, IDENTIFIER, INTEGER, REAL, STRING,
		                   PascalTokenType.NOT, LEFT_PAREN);
	
    public ExpressionParser(PascalParserTD parent)
    {
        super(parent);
    }

    /**
     * 解析一个表达式，如语法图中的expression/term/factor等
     * @param token 表达式第一个token
     * @return 表达式分析子树根节点
     * @throws Exception
     */
    public ICodeNode parse(Token token)
        throws Exception
    {
        return parseExpression(token);
    }

    // 关系操作符集合
    private static final EnumSet<PascalTokenType> REL_OPS =
        EnumSet.of(EQUALS, NOT_EQUALS, LESS_THAN, LESS_EQUALS,
                   GREATER_THAN, GREATER_EQUALS);

    // Token操作符与分析树节点的映射
    private static final HashMap<PascalTokenType, ICodeNodeType>
        REL_OPS_MAP = new HashMap<PascalTokenType, ICodeNodeType>();
    static {
        REL_OPS_MAP.put(EQUALS, EQ);
        REL_OPS_MAP.put(NOT_EQUALS, NE);
        REL_OPS_MAP.put(LESS_THAN, LT);
        REL_OPS_MAP.put(LESS_EQUALS, LE);
        REL_OPS_MAP.put(GREATER_THAN, GT);
        REL_OPS_MAP.put(GREATER_EQUALS, GE);
    };

    /**
     * @param token 初始token
     * @return 表达式分析子树根节点
     * @throws Exception if an error occurred.
     */
    private ICodeNode parseExpression(Token token)
        throws Exception
    {
        //参见语法图5-2的语法规则，首先是一个simpleExpression，如果有关系
    	//操作符，还有一个用来比较的simpleExpression
        ICodeNode rootNode = parseSimpleExpression(token);
        //检测类型
        TypeSpec resultType = rootNode != null ? rootNode.getTypeSpec()
                : Predefined.undefinedType;
        token = currentToken();
        TokenType tokenType = token.getType();

        // 有比较
        if (REL_OPS.contains(tokenType)) {

            // 如果有比较操作符，则比较符是根节点，两个simpleExpression分别作为左右子节点
        	//比如 a > b，那么分析树为  >
        	//                 / \
        	//                a   b
            ICodeNodeType nodeType = REL_OPS_MAP.get(tokenType);
            ICodeNode opNode = ICodeFactory.createICodeNode(nodeType);
            opNode.addChild(rootNode);
            //跳过比较符
            token = nextToken();  
            ICodeNode simExprNode = parseSimpleExpression(token);
            opNode.addChild(simExprNode);
            rootNode = opNode;
            
            TypeSpec simExprType = simExprNode != null
                    ? simExprNode.getTypeSpec()
                    : Predefined.undefinedType;
            //加入类型检查，比较操作符的两端是否类型兼容         
            if (TypeChecker.areComparisonCompatible(resultType, simExprType)) {
            		resultType = Predefined.booleanType;
            }else {
            	errorHandler.flag(token, PascalErrorCode.INCOMPATIBLE_TYPES, this);
            	resultType = Predefined.undefinedType;
            }
        }
        if (rootNode != null) {
            rootNode.setTypeSpec(resultType);
        }
        return rootNode;
    }

    // 加法类运算符
    private static final EnumSet<PascalTokenType> ADD_OPS =
        EnumSet.of(PLUS, MINUS, PascalTokenType.OR);

    // 加法类运算符toke和对应分析树节点之间的映射
    private static final HashMap<PascalTokenType, ICodeNodeTypeImpl>
        ADD_OPS_OPS_MAP = new HashMap<PascalTokenType, ICodeNodeTypeImpl>();
    static {
        ADD_OPS_OPS_MAP.put(PLUS, ADD);
        ADD_OPS_OPS_MAP.put(MINUS, SUBTRACT);
        ADD_OPS_OPS_MAP.put(PascalTokenType.OR, ICodeNodeTypeImpl.OR);
    };

    /**
     * 解析一个简单表达式
     * @param token 表达式第一个token
     * @return 简单表达式分析子树根节点
     * @throws Exception 
     */
    private ICodeNode parseSimpleExpression(Token token)
        throws Exception
    {
    	//参见语法图5-2 的语法规则
        TokenType signType = null; 
        Token signToken = null;
        //探测前面可选的正/负 符号
        TokenType tokenType = token.getType();
        if ((tokenType == PLUS) || (tokenType == MINUS)) {
            signType = tokenType;
            signToken = token;
            token = nextToken(); 
        }
        // 紧接着的一个term
        ICodeNode rootNode = parseTerm(token);
        TypeSpec resultType = rootNode != null ? rootNode.getTypeSpec() : Predefined.undefinedType;
        //既然都有符号位+-了，那么后面的必须是数字才行
        if (null != signType && !TypeChecker.isIntegerOrReal(resultType)){
        	errorHandler.flag(signToken, PascalErrorCode.INCOMPATIBLE_TYPES,this);
        }
        // 正号忽略掉，负号须创建一个表示反值运算的"NEGATE”节点
        if (signType == MINUS) {
            ICodeNode negateNode = ICodeFactory.createICodeNode(NEGATE);
            negateNode.addChild(rootNode);
            negateNode.setTypeSpec(rootNode.getTypeSpec()); //负号不改变类型
            rootNode = negateNode;
        }
        
        token = currentToken();
        tokenType = token.getType();

        // 探测有没有加法类运算，如果有，加法类运算符后面还有另一个term,并以运算符作为根节点。
        // 如果类似于EBNF语法 (OP TERM)+的多个，请留意它根节点的变化，是从右到左的。
        while (ADD_OPS.contains(tokenType)) {
        	TokenType operator = tokenType;
            ICodeNodeType nodeType = ADD_OPS_OPS_MAP.get(tokenType);
            ICodeNode opNode = ICodeFactory.createICodeNode(nodeType);
            opNode.addChild(rootNode);
            //跳过运算符
            token = nextToken(); 
            // 另一个term
            ICodeNode termNode = parseTerm(token);
            opNode.addChild(termNode);
            TypeSpec termType = null != termNode ? termNode.getTypeSpec() : Predefined.undefinedType;
            rootNode = opNode;
            switch ((PascalTokenType)operator){
            //加减号的语法检查一样
            case PLUS:
            case MINUS:
            	//两个都是整数，结果还是整数
            	if (TypeChecker.areBothInteger(resultType, termType)){
            		resultType = Predefined.integerType;
            	}else if (TypeChecker.isAtLeastOneReal(resultType, termType)){ //有一个实数，结果肯定是实数，数系扩充
            		resultType = Predefined.realType;
            	}else{
            		errorHandler.flag(token, PascalErrorCode.INCOMPATIBLE_TYPES, this);
            	}
            	break;
            case OR: //OR是加法类操作符，我吐血啊
            	if(TypeChecker.areBothBoolean(resultType, termType)){
            		resultType = Predefined.booleanType;
            	}else{
            		errorHandler.flag(token, PascalErrorCode.INCOMPATIBLE_TYPES, this);
            	}
            	break;
            }
            rootNode.setTypeSpec(resultType);
            
            token = currentToken();
            tokenType = token.getType();
        }

        return rootNode;
    }
    // 乘法类运算符
    private static final EnumSet<PascalTokenType> MULT_OPS =
        EnumSet.of(STAR, SLASH, DIV, PascalTokenType.MOD, PascalTokenType.AND);
    //惩罚类运算符与分析树节点映射
    private static final HashMap<PascalTokenType, ICodeNodeType>
        MULT_OPS_OPS_MAP = new HashMap<PascalTokenType, ICodeNodeType>();
    static {
        MULT_OPS_OPS_MAP.put(STAR, MULTIPLY);
        MULT_OPS_OPS_MAP.put(SLASH, FLOAT_DIVIDE);
        MULT_OPS_OPS_MAP.put(DIV, INTEGER_DIVIDE);
        MULT_OPS_OPS_MAP.put(PascalTokenType.MOD, ICodeNodeTypeImpl.MOD);
        MULT_OPS_OPS_MAP.put(PascalTokenType.AND, ICodeNodeTypeImpl.AND);
    };

    /**
     * 解析一个term
     * @param token term首token
     * @return term分析子树根节点
     * @throws Exception
     */
    private ICodeNode parseTerm(Token token)
        throws Exception
    {
        //请对照语法图5-2规则看代码
        ICodeNode rootNode = parseFactor(token);
        TypeSpec resultType = rootNode != null ? rootNode.getTypeSpec()
                : Predefined.undefinedType;
        token = currentToken();
        TokenType tokenType = token.getType();

        //(OP FACTOR)+，与解析简单表达式后的term基本一样
        while (MULT_OPS.contains(tokenType)) {
        	 TokenType operator = tokenType;
            ICodeNodeType nodeType = MULT_OPS_OPS_MAP.get(tokenType);
            ICodeNode opNode = ICodeFactory.createICodeNode(nodeType);
            opNode.addChild(rootNode);
            token = nextToken();  
            ICodeNode factorNode = parseFactor(token);
            opNode.addChild(factorNode);
            TypeSpec factorType = factorNode != null ? factorNode.getTypeSpec()
                    : Predefined.undefinedType;
            rootNode = opNode;
            
            //判断结果类型
            switch ((PascalTokenType) operator) {

                case STAR: {
                    // 两个操作数都是整数
                    if (TypeChecker.areBothInteger(resultType, factorType)) {
                        resultType = Predefined.integerType;
                    }else if (TypeChecker.isAtLeastOneReal(resultType,
                                                          factorType)) {
                        resultType = Predefined.realType;
                    }else {
                        errorHandler.flag(token, PascalErrorCode.INCOMPATIBLE_TYPES, this);
                    }
                    break;
                }

                case SLASH: {
                    // 两个整数，或者有个实数，结果为实数
                    if (TypeChecker.areBothInteger(resultType, factorType) ||
                        TypeChecker.isAtLeastOneReal(resultType, factorType))
                    {
                        resultType = Predefined.realType;
                    }else {
                        errorHandler.flag(token,  PascalErrorCode.INCOMPATIBLE_TYPES, this);
                    }
                    break;
                }
                case DIV:
                case MOD: {
                    //两个为整数取模相除，结果为整数
                    if (TypeChecker.areBothInteger(resultType, factorType)) {
                        resultType = Predefined.integerType;
                    }else {
                        errorHandler.flag(token,  PascalErrorCode.INCOMPATIBLE_TYPES, this);
                    }
                    break;
                }

                case AND: {
                    // 并操作符要求两边都是boolean，结果一定是boolean
                    if (TypeChecker.areBothBoolean(resultType, factorType)) {
                        resultType = Predefined.booleanType;
                    }else {
                        errorHandler.flag(token,  PascalErrorCode.INCOMPATIBLE_TYPES, this);
                    }
                    break;
                }
            }
            rootNode.setTypeSpec(resultType);
            
            token = currentToken();
            tokenType = token.getType();
        }

        return rootNode;
    }

    /**
     * 解析factor，后续的factor实现会更复杂
     * @param token factor首token.
     * @return factor分析子树根节点
     * @throws Exception
     */
    private ICodeNode parseFactor(Token token)
        throws Exception
    {
        TokenType tokenType = token.getType();
        ICodeNode rootNode = null;
        switch ((PascalTokenType) tokenType) {
            case IDENTIFIER: { //变量
                return parseIdentifier(token);
            }
            case INTEGER: { //整数
                rootNode = ICodeFactory.createICodeNode(INTEGER_CONSTANT);
                rootNode.setAttribute(VALUE, token.getValue());
                token = nextToken(); 
                rootNode.setTypeSpec(Predefined.integerType);
                break;
            }
            case REAL: {//浮点数
                rootNode = ICodeFactory.createICodeNode(REAL_CONSTANT);
                rootNode.setAttribute(VALUE, token.getValue());
                token = nextToken();  
                rootNode.setTypeSpec(Predefined.realType);
                break;
            }
            case STRING: {//字符串
                String value = (String) token.getValue();
                rootNode = ICodeFactory.createICodeNode(STRING_CONSTANT);
                rootNode.setAttribute(VALUE, value);
                //长度为1则是char，大于1是字符串
                TypeSpec resultType = value.length() == 1
                        ? Predefined.charType
                        : TypeFactory.createStringType(value);
                token = nextToken(); 
                rootNode.setTypeSpec(resultType);
                break;
            }
            case NOT: {//反值 !FACTOR
            	//跳过NOT
                token = nextToken(); 
                rootNode = ICodeFactory.createICodeNode(ICodeNodeTypeImpl.NOT);
                //解析NOT后面的Factor
                ICodeNode factorNode = parseFactor(token);
                rootNode.addChild(factorNode);
                TypeSpec factorType = factorNode != null
                        ? factorNode.getTypeSpec()
                        : Predefined.undefinedType;
                //NOT的右边一定得是个布尔值
				if (!TypeChecker.isBoolean(factorType)) {
				  errorHandler.flag(token, PascalErrorCode.INCOMPATIBLE_TYPES, this);
				}
				rootNode.setTypeSpec(Predefined.booleanType);
                break;
            }
            case LEFT_PAREN: {//改变优先级的括号()
            	//跳过左边括号(
                token = nextToken(); 
                rootNode = parseExpression(token);
                //这个语句没有任何意义，为了保留作者意图
                TypeSpec resultType = rootNode != null
                        ? rootNode.getTypeSpec()
                        : Predefined.undefinedType;
                //跳过右边括号)
                token = currentToken();
                if (token.getType() == RIGHT_PAREN) {
                    token = nextToken();  // consume the )
                }
                else {
                    errorHandler.flag(token, MISSING_RIGHT_PAREN, this);
                }
                //这个语句没有任何意义，为了保留作者意图
                rootNode.setTypeSpec(resultType);
                break;
            }

            default: {
                errorHandler.flag(token, UNEXPECTED_TOKEN, this);
                break;
            }
        }

        return rootNode;
    }
    /**
     * 解析一个标识符
     * @param token 当前token
     * @return the root 分析树的根节点
     * @throws Exception if an error occurred.
     */
    private ICodeNode parseIdentifier(Token token)
        throws Exception
    {
        ICodeNode rootNode = null;

        //在栈中找这个标识符
        String name = token.getText().toLowerCase();
        SymTabEntry id = symTabStack.lookup(name);

        //未定义
        if (id == null) {
            errorHandler.flag(token, IDENTIFIER_UNDEFINED, this);
            id = symTabStack.enterLocal(name);
            id.setDefinition(DefinitionImpl.UNDEFINED);
            id.setTypeSpec(Predefined.undefinedType);
        }

        Definition defnCode = id.getDefinition();

        switch ((DefinitionImpl) defnCode) {

            case CONSTANT: {
                Object value = id.getAttribute(SymTabKeyImpl.CONSTANT_VALUE);
                TypeSpec type = id.getTypeSpec();

                if (value instanceof Integer) {
                    rootNode = ICodeFactory.createICodeNode(INTEGER_CONSTANT);
                    rootNode.setAttribute(VALUE, value);
                }
                else if (value instanceof Float) {
                    rootNode = ICodeFactory.createICodeNode(REAL_CONSTANT);
                    rootNode.setAttribute(VALUE, value);
                }
                else if (value instanceof String) {
                    rootNode = ICodeFactory.createICodeNode(STRING_CONSTANT);
                    rootNode.setAttribute(VALUE, value);
                }

                id.appendLineNumber(token.getLineNumber());
                token = nextToken();  

                if (rootNode != null) {
                    rootNode.setTypeSpec(type);
                }

                break;
            }
            //枚举常量
            case ENUMERATION_CONSTANT: {
                Object value = id.getAttribute(SymTabKeyImpl.CONSTANT_VALUE);
                TypeSpec type = id.getTypeSpec();

                rootNode = ICodeFactory.createICodeNode(INTEGER_CONSTANT);
                rootNode.setAttribute(VALUE, value);

                id.appendLineNumber(token.getLineNumber());
                token = nextToken();  

                rootNode.setTypeSpec(type);
                break;
            }
            //默认变量
            default: {
                VariableParser variableParser = new VariableParser(this);
                rootNode = variableParser.parse(token, id);
                break;
            }
        }

        return rootNode;
    }
}
